package checkers_seperate_classes;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableModel;

import checkers_seperate_classes.CheckersData;
import checkers_seperate_classes.CheckersData.SquareContent;
import checkers_seperate_classes.CheckersMove;


/**
 * This panel displays a 160-by-160 checkerboard pattern with
 * a 2-pixel black border.  It is assumed that the size of the
 * panel is set to exactly 164-by-164 pixels.  This class does
 * the work of letting the users play checkers, and it displays
 * the checkerboard.
 */
public class Board extends JPanel implements ActionListener, MouseListener {


	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	CheckersData board;  // The data for the checkers board is kept here.
	//    This board is also responsible for generating
	//    lists of legal moves.

	SquareContent currentPlayer;      // Whose turn is it now?  The possible values
	//    are CheckersData.RED and CheckersData.BLACK.

	/* The next three variables are valid only when the game is in progress. */

	boolean gameInProgress; // Is a game currently in progress?

	CheckersMove[] legalMoves;  // An array containing the legal moves for the
	//   current player.

	private JLabel message;  // Label for displaying messages to the user.

	private JScrollPane moveHistoryPane;
	private JTable moveHistoryTable;

	private JButton newGameButton;  // Button for starting a new game.
	private JButton resignButton;   // Button that a player can use to end 
	// the game by resigning.

	int selectedRow, selectedCol;  // If the current player has selected a piece to
	//     move, these give the row and column
	//     containing that piece.  If no piece is
	//     yet selected, then selectedRow is -1.

	private DefaultTableModel tableModel;

	private JButton takeBackMoveButton;

	private SquareContent firstPlayer;


	/**
	 * Constructor.  Create the buttons and label.  Listens for mouse
	 * clicks and for clicks on the buttons.  Create the board and
	 * start the first game.
	 */
	Board() {
		setBackground(Color.BLACK);
		addMouseListener(this);

		resignButton = new JButton("Resign");
		resignButton.addActionListener(this);
		newGameButton = new JButton("New Game");
		newGameButton.addActionListener(this);
		takeBackMoveButton = new JButton("Take Back");
		takeBackMoveButton.addActionListener(this);
		message = new JLabel("",SwingConstants.CENTER);
		message.setFont(new  Font("Serif", Font.BOLD, 14));
		message.setForeground(Color.green);
		board = new CheckersData(CheckersData.SquareContent.RED);

		// Setup the move history table with no data
		Object[][] data = new Object[][] { };
		String columnNames[] = { "Player", "From Row", "From Col", 
				"To Row", "To Col", 
				"Piece Taken","King Promoted","Score" };

		// Create a new table instance
		moveHistoryTable = new JTable( new DefaultTableModel(data, columnNames) );
		moveHistoryTable.addComponentListener(new ComponentAdapter() {
			@Override
			public void componentResized(ComponentEvent e) {
				moveHistoryTable.scrollRectToVisible(moveHistoryTable.getCellRect(moveHistoryTable.getRowCount()-1, 0, true));
			}
		});
		tableModel = (DefaultTableModel) moveHistoryTable.getModel();
		moveHistoryPane = new JScrollPane( moveHistoryTable );

		// Position the buttons
		newGameButton.setBounds(210, 30, 120, 30);
		resignButton.setBounds(210, 90, 120, 30);
		takeBackMoveButton.setBounds(210, 150, 120, 30);
		message.setBounds(0, 200, 350, 30);
		moveHistoryPane.setBounds(20, 250, 600, 180);

		doNewGame();
	}

	public Board(Board aThat) {
		super();
		this.board = new CheckersData(aThat.board);
		this.gameInProgress = aThat.gameInProgress;
		this.currentPlayer = aThat.currentPlayer;
		this.selectedRow = aThat.selectedRow;
		this.selectedCol = aThat.selectedCol;
		this.legalMoves = new CheckersMove[aThat.legalMoves.length];
		for (int i=0; i<aThat.legalMoves.length; i++) {
			this.legalMoves[i] = new CheckersMove(aThat.legalMoves[i]);
		}
		this.newGameButton = aThat.newGameButton;
		this.resignButton = aThat.resignButton;
		this.message = aThat.message;
		this.moveHistoryPane = aThat.moveHistoryPane;
		this.moveHistoryTable = aThat.moveHistoryTable;
		this.tableModel = aThat.tableModel;
		this.takeBackMoveButton = aThat.takeBackMoveButton;
	}





	/**
	 * Respond to user's click on one of the two buttons.
	 */
	@Override
	public void actionPerformed(ActionEvent evt) {
		Object src = evt.getSource();
		if (src == newGameButton)
			doNewGame();
		else if (src == resignButton)
			doResign();
		else if (src == takeBackMoveButton)
			doTakeBackMove();
	}


	void addMoveToTable(CheckersMove aMove, final String pieceTaken) {
		Vector<String> rowData = new Vector<>();
		if (currentPlayer == SquareContent.RED) {
			rowData.add("Red");
		} else {
			rowData.add("Black");
		}
		rowData.add(String.valueOf(aMove.getFromRow()+1));
		rowData.add(String.valueOf(aMove.getFromCol()+1));
		rowData.add(String.valueOf(aMove.getToRow()+1));
		rowData.add(String.valueOf(aMove.getToCol()+1));
		rowData.add(pieceTaken);
		String wasPromotedToKing = "";
		if (board.wasPiecePromotedToKing()) {
			wasPromotedToKing = "true";
		}
		rowData.add(wasPromotedToKing);
		double currentScore = calculateScore();
		rowData.add(String.valueOf(currentScore));
		tableModel.addRow(rowData);
	}


	public CheckersMove bestMove(Board board, int lookAhead)
	{
		CheckersMove[] possibleMoves = board.board.getLegalMoves(board.currentPlayer);
		CheckersMove bestMove = null;
		Double bestScore = -1e10;
		Board boardCp = clone();     

		System.out.println("look ahead is "+lookAhead);
		System.out.println("  board is");
		System.out.println(board.toString(4));
		System.out.println("  "+possibleMoves.length+" possible moves");
		for (int i=0;i<possibleMoves.length; i++) {
			System.out.println("    "+(i+1)+": "+possibleMoves[i]);
		}
		for (CheckersMove move : possibleMoves) {
			System.out.println("       Trial move "+move.toString());
			// just the original code
			if(lookAhead <= 1) {
				boardCp.doMakeMove(move,false);
				System.out.println("Board after trial move with lookahead of "+lookAhead);
				System.out.println(boardCp.toString(6));
				double tempScore = boardCp.board.calculateScore(boardCp.currentPlayer);
				if ( tempScore > bestScore)
				{
					bestMove = move;
					bestScore = tempScore;
				}
				boardCp.doTakeBackMove();
			} else {
				// recursion, can be changed to a loop 
				boardCp.doMakeMove(move,false);                // apply
				System.out.println("Board after trial move with lookahead of "+lookAhead);
				System.out.println(boardCp.toString(6));
				System.out.println("Calling recurssive bestMove with "+(lookAhead-1));
				CheckersMove move2 = bestMove(boardCp, lookAhead-1);     // dive and get best 
				boardCp.doMakeMove(move2,false);                // apply
				double tempScore = boardCp.board.calculateScore(boardCp.currentPlayer);
				boardCp.doTakeBackMove();
				if (tempScore > bestScore)
				{
					bestMove = move2;
					bestScore = tempScore;
				}
				boardCp.doTakeBackMove();
			}
		}
		return bestMove;
	}


	public double calculateScore() {
		double score = this.board.calculateRedScore();
		if (currentPlayer == SquareContent.BLACK) {
			score = -score;
		}
		return score;
	}


	@Override
	public Board clone() {
		Board rtn = new Board(this);
		return rtn;
	}
	/**
	 * This is called by mousePressed() when a player clicks on the
	 * square in the specified row and col.  It has already been checked
	 * that a game is, in fact, in progress.
	 */
	void doClickSquare(int row, int col) {

		/* If the player clicked on one of the pieces that the player
      can move, mark this row and col as selected and return.  (This
      might change a previous selection.)  Reset the message, in
      case it was previously displaying an error message. */

		for (int i = 0; i < legalMoves.length; i++)
			if (legalMoves[i].getFromRow() == row && legalMoves[i].getFromCol() == col) {
				selectedRow = row;
				selectedCol = col;
				if (currentPlayer == SquareContent.RED)
					message.setText("RED:  Make your move.");
				else
					message.setText("BLACK:  Make your move.");
				repaint();
				return;
			}

		/* If no piece has been selected to be moved, the user must first
      select a piece.  Show an error message and return. */

		if (selectedRow < 0) {
			message.setText("Click the piece you want to move.");
			return;
		}

		/* If the user clicked on a square where the selected piece can be
      legally moved, then make the move and return. */

		for (int i = 0; i < legalMoves.length; i++)
			if (legalMoves[i].getFromCol() == selectedRow && legalMoves[i].getFromCol() == selectedCol
			&& legalMoves[i].getToCol() == row && legalMoves[i].getToCol() == col) {
				doMakeMove(legalMoves[i]);
				return;
			}

		/* If we get to this point, there is a piece selected, and the square where
      the user just clicked is not one where that piece can be legally moved.
      Show an error message. */

		message.setText("Click the square you want to move to.");

	}  // end doClickSquare()


	/**
	 * This is called when the current player has chosen the specified
	 * move.  Make the move, and then either end or continue the game
	 * appropriately.
	 */
	void doMakeMove(CheckersMove move) {
		doMakeMove(move, true);
	}

	void doMakeMove(CheckersMove aMove, boolean addMoveToTable) {

		if (tableModel.getRowCount() == 0) {
			takeBackMoveButton.setEnabled(true);
			resignButton.setEnabled(true);
		}

		//		final SquareContent startPlayer = board.getNextMoveWillBeMadeBy();
		board.makeMove(aMove);

		// What piece was taken
		final String pieceTaken = board.getNameOfPieceTaken();

		/* Add the move to the move history */
		if (addMoveToTable) {
			addMoveToTable(aMove, pieceTaken);
			// final int nMovesAhead = 3;
			// generateMoveTree(nMovesAhead);
		}


		/* If the move was a jump, it's possible that the player has another
      jump.  Check for legal jumps starting from the square that the player
      just moved to.  If there are any, the player must jump.  The same
      player continues moving.
		 */

		if (aMove.isJump()) {
			legalMoves = board.getLegalJumpsFrom(currentPlayer,aMove.getToCol(),aMove.getToCol());
			if (legalMoves != null) {
				if (currentPlayer == SquareContent.RED)
					message.setText("RED:  You must continue jumping.");
				else
					message.setText("BLACK:  You must continue jumping.");
				selectedRow = aMove.getToCol();  // Since only one piece can be moved, select it.
				selectedCol = aMove.getToCol();
				repaint();
				return;
			}
		}

		/* The current player's turn is ended, so change to the other player.
      Get that player's legal moves.  If the player has no legal moves,
      then the game ends. */

		if (currentPlayer == SquareContent.RED) {
			currentPlayer = SquareContent.BLACK;
			legalMoves = board.getLegalMoves(currentPlayer);
			if (legalMoves == null)
				gameOver("BLACK has no moves.  RED wins.");
			else if (legalMoves[0].isJump())
				message.setText("BLACK:  Make your move.  You must jump.");
			else
				message.setText("BLACK:  Make your move.");
		}
		else {
			currentPlayer = SquareContent.RED;
			legalMoves = board.getLegalMoves(currentPlayer);
			if (legalMoves == null)
				gameOver("RED has no moves.  BLACK wins.");
			else if (legalMoves[0].isJump())
				message.setText("RED:  Make your move.  You must jump.");
			else
				message.setText("RED:  Make your move.");
		}

		/* Set selectedRow = -1 to record that the player has not yet selected
      a piece to move. */

		selectedRow = -1;

		/* As a courtesy to the user, if all legal moves use the same piece, then
      select that piece automatically so the user won't have to click on it
      to select it. */

		if (legalMoves != null) {
			boolean sameStartSquare = true;
			for (int i = 1; i < legalMoves.length; i++)
				if (legalMoves[i].getFromCol() != legalMoves[0].getFromCol()
				|| legalMoves[i].getFromCol() != legalMoves[0].getFromCol()) {
					sameStartSquare = false;
					break;
				}
			if (sameStartSquare) {
				selectedRow = legalMoves[0].getFromCol();
				selectedCol = legalMoves[0].getFromCol();
			}
		}

		/* Make sure the board is redrawn in its new state. */

		repaint();

	}  // end doMakeMove();

	/**
	 * Start a new game
	 */
	void doNewGame() {
		if (gameInProgress == true) {
			// This should not be possible, but it doesn't hurt to check.
			message.setText("Finish the current game first!");
			return;
		}
		board.setUpGame();   // Set up the pieces.
		currentPlayer = SquareContent.RED;   // RED moves first.
		firstPlayer = currentPlayer;
		legalMoves = board.getLegalMoves(SquareContent.RED);  // Get RED's legal moves.
		selectedRow = -1;   // RED has not yet selected a piece to move.
		message.setText("Red:  Make your move.");
		gameInProgress = true;
		newGameButton.setEnabled(false);
		resignButton.setEnabled(false);
		takeBackMoveButton.setEnabled(false);
		tableModel.setRowCount(0);
		repaint();
	}

	/**
	 * Current player resigns.  Game ends.  Opponent wins.
	 */
	void doResign() {
		if (gameInProgress == false) {  // Should be impossible.
			message.setText("There is no game in progress!");
			return;
		}
		if (currentPlayer == SquareContent.RED)
			gameOver("RED resigns.  BLACK wins.");
		else
			gameOver("BLACK resigns.  RED wins.");
	}

	/**
	 * This is called when to take back the last move.
	 */
	public void doTakeBackMove() {

		if (tableModel.getRowCount() == 0) {
			return;
		}

		final int lastRow = tableModel.getRowCount()-1;
		int iCol = -1;
		iCol++;
		currentPlayer = SquareContent.RED;
		String playerNamePrevioiusMove = (String)tableModel.getValueAt(lastRow, iCol);
		if (playerNamePrevioiusMove.equalsIgnoreCase("black")){
			currentPlayer = SquareContent.BLACK;
		}
		iCol++;
		final int fromRow = Integer.valueOf((String)tableModel.getValueAt(lastRow, iCol))-1;
		iCol++;
		final int fromCol = Integer.valueOf((String)tableModel.getValueAt(lastRow, iCol))-1;
		iCol++;
		final int toRow = Integer.valueOf((String)tableModel.getValueAt(lastRow, iCol))-1;
		iCol++;
		final int toCol = Integer.valueOf((String)tableModel.getValueAt(lastRow, iCol))-1;
		iCol++;
		final String pieceTaken = (String)tableModel.getValueAt(lastRow, iCol);
		String previousPieceTaken = "empty";
		if (lastRow>0) previousPieceTaken = (String)tableModel.getValueAt(lastRow-1, iCol);
		iCol++;
		final boolean promotedToKing = !(((String)tableModel.getValueAt(lastRow, iCol)).isEmpty());
		boolean previousPromotedToKing = false;
		if (lastRow>0) previousPromotedToKing = !(((String)tableModel.getValueAt(lastRow-1, iCol)).isEmpty());
		CheckersMove move = new CheckersMove(fromRow, fromCol, toRow, toCol);
		board.makeUnMove(move,pieceTaken,previousPieceTaken,promotedToKing, previousPromotedToKing);
		tableModel.removeRow(lastRow);

		/* The current player's turn is ended, so change to the other player.
	      Get that player's legal moves.  If the player has no legal moves,
	      then the game ends. */

		if (currentPlayer == SquareContent.RED) {
			legalMoves = board.getLegalMoves(currentPlayer);
			if (legalMoves[0].isJump())
				message.setText("Red:  Make your move.  You must jump.");
			else
				message.setText("Red:  Make your move.");
		}
		else {
			legalMoves = board.getLegalMoves(currentPlayer);
			if (legalMoves[0].isJump())
				message.setText("BLACK:  Make your move.  You must jump.");
			else
				message.setText("BLACK:  Make your move.");
		}

		/* Set selectedRow = -1 to record that the player has not yet selected
	      a piece to move. */

		selectedRow = -1;

		/* As a courtesy to the user, if all legal moves use the same piece, then
	      select that piece automatically so the user won't have to click on it
	      to select it. */

		if (legalMoves != null) {
			boolean sameStartSquare = true;
			for (int i = 1; i < legalMoves.length; i++)
				if (legalMoves[i].getFromCol() != legalMoves[0].getFromCol()
				|| legalMoves[i].getFromCol() != legalMoves[0].getFromCol()) {
					sameStartSquare = false;
					break;
				}
			if (sameStartSquare) {
				selectedRow = legalMoves[0].getFromCol();
				selectedCol = legalMoves[0].getFromCol();
			}
		}

		/* Make sure the board is redrawn in its new state. */

		repaint();

	}  // end doTakeBackMove();

	/**
	 * The game ends.  The parameter, str, is displayed as a message
	 * to the user.  The states of the buttons are adjusted so players
	 * can start a new game.  This method is called when the game
	 * ends at any point in this class.
	 */
	void gameOver(String str) {
		message.setText(str);
		newGameButton.setEnabled(true);
		resignButton.setEnabled(false);
		gameInProgress = false;
	}

	public JButton getGetResignButton() {
		return resignButton;
	} 

	public JLabel getMessage() {
		return message;
	}
	public JScrollPane getMoveHistoryTablePane() {
		return moveHistoryPane;
	}


	public int getMoveNumber() {
		return this.board.getMoveNumber();
	}


	public JButton getNewGameButton() {
		return newGameButton;
	}


	public SquareContent getNextPlayer() {
		SquareContent nextPlayer = currentPlayer;
		return nextPlayer;
	}


	public JButton getTakeBackMoveButton() {
		return takeBackMoveButton;
	}
	@Override
	public void mouseClicked(MouseEvent evt) { }
	@Override
	public void mouseEntered(MouseEvent evt) { }
	@Override
	public void mouseExited(MouseEvent evt) { }


	/**
	 * Respond to a user click on the board.  If no game is in progress, show 
	 * an error message.  Otherwise, find the row and column that the user 
	 * clicked and call doClickSquare() to handle it.
	 */
	@Override
	public void mousePressed(MouseEvent evt) {
		if (gameInProgress == false)
			message.setText("Click \"New Game\" to start a new game.");
		else {
			int col = (evt.getX() - 2) / 20;
			int row = (evt.getY() - 2) / 20;
			if (col >= 0 && col < 8 && row >= 0 && row < 8)
				doClickSquare(row,col);
		}
	}


	@Override
	public void mouseReleased(MouseEvent evt) { }

	/**
	 * Draw a checkerboard pattern in gray and lightGray.  Draw the
	 * checkers.  If a game is in progress, hilite the legal moves.
	 */
	@Override
	public void paintComponent(Graphics g) {

		/* Draw a two-pixel black border around the edges of the canvas. */

		g.setColor(Color.black);
		g.drawRect(0,0,getSize().width-1,getSize().height-1);
		g.drawRect(1,1,getSize().width-3,getSize().height-3);

		/* Draw the squares of the checkerboard and the checkers. */

		for (int row = 0; row < 8; row++) {
			for (int col = 0; col < 8; col++) {
				if ( row % 2 == col % 2 )
					g.setColor(Color.LIGHT_GRAY);
				else
					g.setColor(Color.GRAY);
				g.fillRect(2 + col*20, 2 + row*20, 20, 20);
				final SquareContent piece = board.pieceAt(row,col);
				switch (piece) {
				case RED:
					g.setColor(Color.RED);
					g.fillOval(4 + col*20, 4 + row*20, 15, 15);
					break;
				case BLACK:
					g.setColor(Color.BLACK);
					g.fillOval(4 + col*20, 4 + row*20, 15, 15);
					break;
				case RED_KING:
					g.setColor(Color.RED);
					g.fillOval(4 + col*20, 4 + row*20, 15, 15);
					g.setColor(Color.WHITE);
					g.drawString("K", 7 + col*20, 16 + row*20);
					break;
				case BLACK_KING:
					g.setColor(Color.BLACK);
					g.fillOval(4 + col*20, 4 + row*20, 15, 15);
					g.setColor(Color.WHITE);
					g.drawString("K", 7 + col*20, 16 + row*20);
					break;
				case EMPTY:
					break;
				default:
					throw new IllegalStateException("Unknown piece "+piece);
				}
			}
		}

		/* If a game is in progress, hilite the legal moves.   Note that legalMoves
      is never null while a game is in progress. */      

		if (gameInProgress) {
			/* First, draw a 2-pixel cyan border around the pieces that can be moved. */
			g.setColor(Color.cyan);
			for (int i = 0; i < legalMoves.length; i++) {
				g.drawRect(2 + legalMoves[i].getFromCol()*20, 2 + legalMoves[i].getFromCol()*20, 19, 19);
				g.drawRect(3 + legalMoves[i].getFromCol()*20, 3 + legalMoves[i].getFromCol()*20, 17, 17);
			}
			/* If a piece is selected for moving (i.e. if selectedRow >= 0), then
            draw a 2-pixel white border around that piece and draw green borders 
            around each square that that piece can be moved to. */
			if (selectedRow >= 0) {
				g.setColor(Color.white);
				g.drawRect(2 + selectedCol*20, 2 + selectedRow*20, 19, 19);
				g.drawRect(3 + selectedCol*20, 3 + selectedRow*20, 17, 17);
				g.setColor(Color.green);
				for (int i = 0; i < legalMoves.length; i++) {
					if (legalMoves[i].getFromCol() == selectedCol && legalMoves[i].getFromCol() == selectedRow) {
						g.drawRect(2 + legalMoves[i].getToCol()*20, 2 + legalMoves[i].getToCol()*20, 19, 19);
						g.drawRect(3 + legalMoves[i].getToCol()*20, 3 + legalMoves[i].getToCol()*20, 17, 17);
					}
				}
			}
		}

	}  // end paintComponent()

	void setCurrentPlayer(SquareContent aPlayer) {
		currentPlayer = aPlayer;		
	}

	@Override
	public String toString() {
		return toString(0);
	}


	public String toString(int nPreBlank) {
		StringBuffer sb = new StringBuffer();
		StringBuffer preSb = new StringBuffer();
		for (int i=0; i<nPreBlank; i++) preSb.append(' ');
		sb.append('\n');
		sb.append(preSb);
		sb.append("   ");
		for (int col = 0; col < 8; col++) {
			sb.append(" "+(col+1));
		}
		sb.append('\n');
		for (int row = 0; row < 8; row++) {
			sb.append(preSb);
			sb.append((row+1)+"  ");
			for (int col = 0; col < 8; col++) {
				sb.append(' ');
				switch (this.board.pieceAt(row,col)) {
				case RED:
					sb.append('r');
					break;
				case BLACK:
					sb.append('b');
					break;
				case RED_KING:
					sb.append('R');
					break;
				case BLACK_KING:
					sb.append('B');
					break;
				case EMPTY:
					if ( row % 2 == col % 2 )
						// Can move here
						sb.append('.');
					else
						// Can't move here
						sb.append('|'); 
					break;
				}
			}
			sb.append('\n');
		}
		sb.append(preSb);
		String nextPlayer = CheckersData.toName(board.getNextMoveWillBeMadeBy());
		final double score = this.calculateScore();
		sb.append(nextPlayer + " turn "+this.getMoveNumber() + ", score "+ score);
		sb.append('\n');
		return sb.toString();
	}


}  // end class Board
